package com.googlecode.gaelloon.controller

import java.lang.reflect.Method
import java.io.InputStream
import java.lang.String
import org.objectweb.asm._
import collection.mutable.{Buffer, ArrayBuffer, HashMap}

class ActionDispatcher(controller: AnyRef) {
  private val clazz: Class[_] = controller.getClass

  def dispatch(action: String, parameters: Map[String, Any]): AnyRef = {
    new MethodResolver(clazz).read
    actionMethod(action).invoke(controller)
  }

  private def actionMethod(action: String) = clazz.getMethod(action)
}

class MethodResolver(clazz: Class[_]) {
  private val methods = HashMap[String, Seq[String]]()

  def read() {
    val cr = new ClassReader(getClassAsStream)
    cr.accept(new MethodCollector(methods), ClassReader.SKIP_FRAMES)
    println(methods)
  }

  private def getClassAsStream: InputStream = {
    val classLoader = clazz.getClassLoader
    val uri = clazz.getName.replace('.', '/') + ".class"

    val is = classLoader.getResourceAsStream(uri)
    if (is == null) throw new RuntimeException("Unable to load class: " + uri) else is
  }

  class MethodCollector(methods: collection.mutable.Map[String, Seq[String]]) extends ClassVisitor {
    def visitMethod(access: Int, name: String, desc: String, signature: String, exceptions: Array[String]) = {
      if (((access & Opcodes.ACC_PUBLIC) != 0) && ((access & Opcodes.ACC_STATIC) == 0)) {
        val parameters = new ArrayBuffer[String]
        methods += (name -> parameters)
//        val types = Type.getArgumentTypes(desc)
//        types.foreach(t => print(t))
//        println(name)
        new MethodParametersCollector(parameters, Type.getArgumentsAndReturnSizes(desc))
      } else null
    }

    def visitEnd = {
    }

    def visitField(p1: Int, p2: String, p3: String, p4: String, p5: AnyRef) = null

    def visitInnerClass(p1: String, p2: String, p3: String, p4: Int) = {
    }

    def visitAttribute(p1: Attribute) = {
    }

    def visitAnnotation(p1: String, p2: Boolean) = null

    def visitOuterClass(p1: String, p2: String, p3: String) = {
    }

    def visitSource(p1: String, p2: String) = {
    }

    def visit(p1: Int, p2: Int, p3: String, p4: String, p5: String, p6: Array[String]) = {
    }
  }

  class MethodParametersCollector(parameters: Buffer[String], lastIndex: Int) extends MethodVisitor {
    def visitLocalVariable(name: String, desc: String, signature: String, start: Label, end: Label, index: Int) = {
      if (index > 0 && index < lastIndex) {
        parameters += name
      }
    }

    def visitEnd = {}

    def visitMaxs(p1: Int, p2: Int) = {}

    def visitLineNumber(p1: Int, p2: Label) = {}

    def visitTryCatchBlock(p1: Label, p2: Label, p3: Label, p4: String) = {}

    def visitMultiANewArrayInsn(p1: String, p2: Int) = {}

    def visitLookupSwitchInsn(p1: Label, p2: Array[Int], p3: Array[Label]) = {}

    def visitTableSwitchInsn(p1: Int, p2: Int, p3: Label, p4: Array[Label]) = {}

    def visitIincInsn(p1: Int, p2: Int) = {}

    def visitLdcInsn(p1: AnyRef) = {}

    def visitLabel(p1: Label) = {}

    def visitJumpInsn(p1: Int, p2: Label) = {}

    def visitMethodInsn(p1: Int, p2: String, p3: String, p4: String) = {}

    def visitFieldInsn(p1: Int, p2: String, p3: String, p4: String) = {}

    def visitTypeInsn(p1: Int, p2: String) = {}

    def visitVarInsn(p1: Int, p2: Int) = {}

    def visitIntInsn(p1: Int, p2: Int) = {}

    def visitInsn(p1: Int) = {}

    def visitFrame(p1: Int, p2: Int, p3: Array[AnyRef], p4: Int, p5: Array[AnyRef]) = {}

    def visitCode = {}

    def visitAttribute(p1: Attribute) = {}

    def visitParameterAnnotation(p1: Int, p2: String, p3: Boolean) = null

    def visitAnnotation(p1: String, p2: Boolean) = null

    def visitAnnotationDefault = null
  }
}